from wormbane.Feature import Doorway
from wormbane.geometry import Vector
from wormbane.Item import Corpse
from wormbane.move import move_item, move_monster
import random


def _passable(tile, thing, diagonal):
    return ((thing.env is tile
             or (not tile.monster
                 and (not tile.top_item or tile.top_item.passable)))
            and (not tile.feature
                 or (tile.feature.diagonally_passable if diagonal
                     else tile.feature.passable)))


def do_close_door(monster, door, game):
    if door.closed:
        raise ActionError()
    if door.tile.monster or door.tile.top_item:
        raise ActionError()
    game.write('%s closes %s', monster, door)
    door.locked = False
    door.closed = True
    monster.time += 1.0 / monster.speed


def do_hit_monster(attacker, defender, game):
    game.write('%s hits %s', attacker, defender)
    vitality_loss = attacker.damage / defender.protection
    vitality_loss = random.uniform(vitality_loss / 2.0, vitality_loss)
    defender.vitality -= vitality_loss
    attacker.time += 1.0 / attacker.speed
    if defender.vitality <= 0.0:
        game.write('%s dies', defender)
        defender.alive = False
        env = defender.env
        move_monster(game, defender, None)
        game.current_level.remove_monster(defender)
        corpse = Corpse()
        corpse.color = defender.color
        move_item(game, corpse, env)
        if defender.is_nemesis:
            game.hero.is_nemesis_nemesis = True
            game.write('Your work here is done.')


def do_kick_door(monster, door, game):
    game.write('%s kicks %s', monster, door)
    if random.randrange(2):
        game.write('%s splinters', door)
        door.env.feature = Doorway(dir = door.dir)
    monster.time += 1.0 / monster.speed


def do_move(monster, tile, game):
    offset = Vector(tile.pos) - Vector(monster.pos)
    diagonal = offset.x and offset.y
    if (_passable(monster.env, monster, diagonal)
        and _passable(tile, monster, diagonal)):
        move_monster(game, monster, tile)
        monster.time += abs(offset) / monster.speed
    else:
        raise ActionError()


def do_open_door(monster, door, game):
    if not door.closed or door.locked:
        raise ActionError()
    game.write('%s opens %s', monster, door)
    door.locked = False
    door.closed = False
    monster.time += 1.0 / monster.speed


def do_push_boulder(monster, boulder, game):
    game.write('%s pushes %s', monster, boulder)
    offset = Vector(boulder.pos) - Vector(monster.pos)
    dest = game.current_level.find_tile(Vector(boulder.pos) + offset)
    diagonal = offset.x and offset.y
    monster.time += abs(offset) / monster.speed
    if (dest is None or not _passable(dest, boulder, diagonal)
        or not _passable(boulder.env, boulder, diagonal)
        or not _passable(monster.env, monster, diagonal)):
        game.write('%s will not move', boulder)
        return
    monster_dest = boulder.env
    move_item(game, boulder, dest)
    if _passable(monster_dest, monster, diagonal):
        move_monster(game, monster, monster_dest)


def do_rest(monster, game):
    game.write('%s rests', monster)
    monster.time += 1.0 / monster.speed
    if not monster.hungry:
        monster.vitality = min(monster.vitality + 0.01, 1.0)


def do_search(monster, game):
    game.write('%s searches', monster)
    tiles = list(monster.tile.neighbors)
    random.shuffle(tiles)
    for tile in tiles:
        if random.randrange(2):
            finding = tile.search()
            if finding is not None:
                game.write('%s finds %?s', monster, finding)
                break


class ActionError(Exception):
    pass
